package middleware

import (
	"github.com/form3tech-oss/jwt-go"
	"go-zero-project/common/ctxdata"
	"go-zero-project/common/excption"
	"go-zero-project/common/response"
	"net/http"
	"strconv"
	"strings"
)

type JwtAuthMiddleware struct {
	signingKey string
	whiteList  []string
}

type CustomClaims struct {
	UserId int64 `json:"jwtUserId"`
	jwt.StandardClaims
}

func NewJwtAuthMiddleware(signingKey string, whiteList []string) *JwtAuthMiddleware {

	return &JwtAuthMiddleware{signingKey: signingKey, whiteList: whiteList}
}

func (m *JwtAuthMiddleware) Handle(next http.HandlerFunc) http.HandlerFunc {
	return func(writer http.ResponseWriter, request *http.Request) {
		uri := request.RequestURI
		isInWhiteList := false
		//判断请求是否包含白名单中的元素
		for _, v := range m.whiteList {
			if strings.Contains(uri, v) {
				isInWhiteList = true
			}
		}
		//如果爱白名单里面直接通过
		if isInWhiteList {
			next(writer, request)
			return
		}
		//否则获取前端header 里面的X-Token字段,这个就是token
		token := request.Header.Get("Authorization")
		customClaims, err := m.parseToken(token)
		if err != nil {
			// 异常返回
			response.Response(writer, nil, err)
			return
		}
		// 设置到header里面
		writer.Header().Set(ctxdata.CtxKeyJwtUserId, strconv.FormatInt(customClaims.UserId, 10))
		next(writer, request)
	}
}

// ParseToken 解析token
func (m *JwtAuthMiddleware) parseToken(token string) (*CustomClaims, error) {
	claims, parseErr := jwt.ParseWithClaims(token, &CustomClaims{}, func(token *jwt.Token) (interface{}, error) {
		return []byte(m.signingKey), nil
	})
	if parseErr != nil {
		if ve, ok := parseErr.(*jwt.ValidationError); ok {
			if ve.Errors&jwt.ValidationErrorMalformed != 0 {
				return nil, excption.NewErrCode(excption.TokenMalformed)
			} else if ve.Errors&jwt.ValidationErrorExpired != 0 {
				// Token is expired
				return nil, excption.NewErrCode(excption.TokenExpireError)
			} else if ve.Errors&jwt.ValidationErrorNotValidYet != 0 {
				return nil, excption.NewErrCode(excption.TokenNotValidYet)
			} else {
				return nil, excption.NewErrCode(excption.TokenInvalidError)
			}
		}

	}
	if claims != nil {
		if c, ok := claims.Claims.(*CustomClaims); ok && claims.Valid {
			return c, nil
		}
		return nil, excption.NewErrCode(excption.TokenInvalidError)
	}
	return nil, excption.NewErrCode(excption.TokenInvalidError)
}
